OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0
以下來自教學文件的一句話:
注意:特徵類似於其他語言常稱作介面(interfaces)的功能,但還是有些差異。
確實,先前我是把特徵類比成其他語言中的interface
,今天來理解Rust中的ㄊ中的特徵(trait)是什麼樣的功能。
以下是一個範例,我們定義幾個多邊形,然後一個叫做Drawable
的特徵:
struct Vertex {
x: f32,
y: f32,
}
struct Rectangle {
origin: Vertex,
width: f32,
height: f32,
}
struct Circle {
origin: Vertex,
radius: f32,
}
trait Drawable {
fn draw(&self);
}
以下是矩形跟圓形對Drawable
的實作:
impl Drawable for Rectangle {
fn draw(&self) {
println!(
"I'm rectangle. My origin is ({}, {}). My width is {} and height is {}.",
self.origin.x, self.origin.y, self.width, self.height
);
}
}
impl Drawable for Circle {
fn draw(&self) {
println!(
"I'm circle. My origin is ({}, {}). My radius is {}.",
self.origin.x, self.origin.y, self.radius
);
}
}
// ...
fn main() {
let rect = Rectangle {
origin: Vertex { x: 0.0, y: 0.0 },
width: 4.0,
height: 3.0,
};
let circle = Circle {
origin: Vertex { x: 5.0, y: 5.0 },
radius: 3.0,
};
rect.draw();
circle.draw();
}
也可以預設特徵的實作:
trait Drawable {
fn draw(&self) {
println!("Nothing!");
}
}
下面定義一個空的結構,空的結構去實作預設特徵的話,是可以直接使用的:
struct Nothing;
trait Drawable {
fn draw(&self) {
println!("Nothing!");
}
}
impl Drawable for Nothing {}
// ...
// in main function
let nothing = Nothing;
nothing.draw();
// output:
// Nothing!
同其他語言,例如C#,我們可以把特徵當作參數,傳進函式內,表示有實作這個特徵的型別才可以使用:
fn draw_shape(shape: &impl Drawable) {
shape.draw();
}
// ...
draw_shape(&rect);
昨天介紹泛型的時候,對於max
的實作,我們可以加上限制,也就是對這個型別是否有實作特徵
// `+`表示多個限制
fn max<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut max = list[0];
for &item in list {
if item > max {
max = item;
}
}
max
}
當然,可以把特徵作為參數,也等同於做了限制:
// 回傳的值,也是標示有實作這兩個特徵的型別
fn max(list: &[(impl PartialOrd + Copy)]) -> (impl PartialOrd + Copy) {
let mut max = list[0];
for &item in list {
if item > max {
max = item;
}
}
max
}
也可以使用where
,可以更明瞭的知道限制了那些特徵:
fn max<T>(list: &[T]) -> T
where T:
Copy + PartialOrd
{
let mut max = list[0];
for &item in list {
if item > max {
max = item;
}
}
max
}
std::fmt::Display
關於上面Rectangle
與Circle
對Drawable
的實作,需要把origin
拆開來,是因為我們並不能直接輸出Vertex
型別,因為沒有對它做std::fmt::Display
的實作,於是把它加上實作:
use std::fmt;
impl fmt::Display for Vertex {
fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
這樣就可以把Rectangle
的實作改成這樣,Circle
同理:
impl Drawable for Rectangle {
fn draw(&self) {
println!(
"I'm rectangle. My origin is {}. My width is {} and height is {}.",
self.origin, self.width, self.height
);
}
}